#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
乳腺癌诊断数据分析与预测
使用朴素贝叶斯分类算法实现乳腺癌诊断结果的分类预测
"""

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix, classification_report
import warnings
warnings.filterwarnings('ignore')

# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

# 创建输出目录
import os
if not os.path.exists('output'):
    os.makedirs('output')

def main():
    # 1. 业务理解
    print("="*50)
    print("乳腺癌诊断数据分析与预测")
    print("目的：利用朴素贝叶斯算法对乳腺癌诊断结果进行分类预测")
    print("="*50)
    
    # 2. 数据读入
    print("\n2. 数据读入")
    # 读取CSV文件
    df = pd.read_csv("bc_data1.csv", header=None)
    print("数据集前5行：")
    print(df.head())
    
    # 3. 数据理解
    print("\n3. 数据理解")
    # 定义特征名称
    cell_feat = ['radius', 'texture', 'perimeter', 'area', 'smoothness', 
                'compactness', 'concavity', 'concave_points', 'symmetry', 
                'fractal_dimension']
    
    # 构建特征列表
    all_feat = ['id', 'diagnosis']
    for stat in ['mean_', 'se_', 'largest_']:
        for feat in cell_feat:
            all_feat.append(stat + feat)
    
    # 设置列名
    df.columns = all_feat
    print("设置列名后的数据：")
    print(df.head())
    
    # 检查数据基本信息
    print("\n数据集基本信息：")
    print(df.info())
    
    # 检查是否有缺失值
    print("\n检查缺失值：")
    print(df.isnull().any())
    
    # 数据描述性统计
    print("\n数据描述性统计：")
    print(df.describe())
    
    # 查看数据形状
    print("\n数据形状：")
    print(df.shape)
    
    # 4. 数据准备
    print("\n4. 数据准备")
    # 将诊断结果编码为数值（M=1表示恶性，B=0表示良性）
    encoder = LabelEncoder()
    df['diagnosis'] = encoder.fit_transform(df['diagnosis'])
    print("编码后的诊断结果（0=良性，1=恶性）：")
    print(df[['id', 'diagnosis']].head())
    
    # 分析诊断结果分布
    diagnosis_count = df['diagnosis'].value_counts()
    print("\n诊断结果分布：")
    print(diagnosis_count)
    
    # 可视化诊断结果分布 - 饼图
    plt.figure(figsize=(10, 6))
    plt.subplot(1, 2, 1)
    sns.countplot(x='diagnosis', data=df, palette=['lightblue', 'salmon'])
    plt.title('诊断结果分布 (柱状图)')
    plt.xlabel('诊断结果 (0=良性, 1=恶性)')
    plt.ylabel('数量')
    
    plt.subplot(1, 2, 2)
    plt.pie(diagnosis_count.values, labels=['良性', '恶性'], autopct='%1.1f%%', 
            colors=['lightblue', 'salmon'], startangle=90, explode=[0.05, 0])
    plt.title('诊断结果分布 (饼图)')
    plt.tight_layout()
    plt.savefig('output/diagnosis_distribution.png', dpi=300)
    
    # 特征相关性分析
    plt.figure(figsize=(14, 12))
    mean_features = [col for col in df.columns if 'mean_' in col]
    correlation = df[mean_features].corr()
    sns.heatmap(correlation, annot=True, cmap='coolwarm', fmt='.2f')
    plt.title('均值特征相关性热图')
    plt.tight_layout()
    plt.savefig('output/correlation_heatmap.png', dpi=300)
    
    # 特征分布可视化
    plt.figure(figsize=(15, 10))
    for i, feature in enumerate(mean_features[:6]):  # 只展示前6个特征
        plt.subplot(2, 3, i+1)
        sns.histplot(data=df, x=feature, hue='diagnosis', kde=True, palette=['lightblue', 'salmon'])
        plt.title(f'{feature} 分布')
    plt.tight_layout()
    plt.savefig('output/feature_distribution.png', dpi=300)
    
    # 准备特征和目标变量
    X = df.iloc[:, 2:]  # 除去id和diagnosis列
    y = df['diagnosis']
    
    # 划分训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.3, random_state=42, stratify=y
    )
    print(f"\n训练集大小: {X_train.shape}, 测试集大小: {X_test.shape}")
    
    # 5. 模型训练
    print("\n5. 模型训练")
    # 初始化高斯朴素贝叶斯模型
    gnb = GaussianNB()
    
    # 训练模型
    gnb.fit(X_train, y_train)
    print("基础模型训练完成")
    
    # 6. 模型评价
    print("\n6. 模型评价")
    # 在测试集上进行预测
    y_pred = gnb.predict(X_test)
    
    # 计算评价指标
    accuracy = accuracy_score(y_test, y_pred)
    precision = precision_score(y_test, y_pred)
    recall = recall_score(y_test, y_pred)
    f1 = f1_score(y_test, y_pred)
    
    print(f"准确率: {accuracy:.6f}")
    print(f"精确率: {precision:.6f}")
    print(f"召回率: {recall:.6f}")
    print(f"F1值: {f1:.6f}")
    
    # 混淆矩阵
    cm = confusion_matrix(y_test, y_pred)
    plt.figure(figsize=(8, 6))
    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
    plt.title('混淆矩阵')
    plt.xlabel('预测标签')
    plt.ylabel('真实标签')
    plt.savefig('output/confusion_matrix.png', dpi=300)
    
    # 分类报告可视化
    report = classification_report(y_test, y_pred, output_dict=True)
    
    # 可视化分类报告
    plt.figure(figsize=(10, 6))
    metrics = ['precision', 'recall', 'f1-score']
    
    # 提取类别0和1的指标
    class_0 = [report['0'][metric] for metric in metrics]
    class_1 = [report['1'][metric] for metric in metrics]
    
    x = np.arange(len(metrics))
    width = 0.35
    
    plt.bar(x - width/2, class_0, width, label='良性(0)', color='lightblue')
    plt.bar(x + width/2, class_1, width, label='恶性(1)', color='salmon')
    
    plt.xlabel('评价指标')
    plt.ylabel('得分')
    plt.title('分类报告')
    plt.xticks(x, metrics)
    plt.legend()
    plt.ylim(0, 1.0)
    
    # 添加数值标签
    for i, v in enumerate(class_0):
        plt.text(i - width/2, v + 0.02, f'{v:.2f}', ha='center')
    
    for i, v in enumerate(class_1):
        plt.text(i + width/2, v + 0.02, f'{v:.2f}', ha='center')
    
    plt.tight_layout()
    plt.savefig('output/classification_report.png', dpi=300)
    
    # 7. 模型调参
    print("\n7. 模型调参")
    # 定义参数网格
    param_grid = {
        'var_smoothing': [1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12]
    }
    
    # 使用网格搜索进行调参
    grid_search = GridSearchCV(
        GaussianNB(), 
        param_grid, 
        cv=5,
        verbose=2
    )
    
    # 训练网格搜索模型
    grid_search.fit(X_train, y_train)
    
    # 输出最佳参数
    print(f"\n最佳参数: {grid_search.best_params_}")
    
    # 可视化最佳参数
    plt.figure(figsize=(10, 6))
    cv_results = pd.DataFrame(grid_search.cv_results_)
    
    # 提取参数和对应的得分
    params = [f"1e-{abs(int(np.log10(p)))}" for p in param_grid['var_smoothing']]
    mean_scores = cv_results['mean_test_score']
    
    plt.plot(params, mean_scores, marker='o', linestyle='-', color='blue')
    plt.axvline(x=f"1e-{abs(int(np.log10(grid_search.best_params_['var_smoothing'])))}", 
                color='red', linestyle='--', label=f"最佳参数: {grid_search.best_params_['var_smoothing']}")
    
    plt.xlabel('var_smoothing 参数值')
    plt.ylabel('交叉验证平均得分')
    plt.title('参数调优结果')
    plt.grid(True)
    plt.legend()
    plt.tight_layout()
    plt.savefig('output/parameter_tuning.png', dpi=300)
    
    # 使用最佳参数的模型
    best_gnb = grid_search.best_estimator_
    
    # 8. 模型预测
    print("\n8. 模型预测")
    # 使用调优后的模型进行预测
    tuned_y_pred = best_gnb.predict(X_test)
    
    # 计算调优后的评价指标
    tuned_accuracy = accuracy_score(y_test, tuned_y_pred)
    tuned_precision = precision_score(y_test, tuned_y_pred)
    tuned_recall = recall_score(y_test, tuned_y_pred)
    tuned_f1 = f1_score(y_test, tuned_y_pred)
    
    print(f"调优后准确率: {tuned_accuracy:.6f}")
    print(f"调优后精确率: {tuned_precision:.6f}")
    print(f"调优后召回率: {tuned_recall:.6f}")
    print(f"调优后F1值: {tuned_f1:.6f}")
    
    # 比较调参前后的性能
    print("\n调参前后性能比较：")
    comparison_data = {
        '指标': ['准确率', '精确率', '召回率', 'F1值'],
        '调参前': [accuracy, precision, recall, f1],
        '调参后': [tuned_accuracy, tuned_precision, tuned_recall, tuned_f1]
    }
    comparison_df = pd.DataFrame(comparison_data)
    print(comparison_df)
    
    # 可视化比较
    plt.figure(figsize=(10, 6))
    metrics = ['准确率', '精确率', '召回率', 'F1值']
    before = [accuracy, precision, recall, f1]
    after = [tuned_accuracy, tuned_precision, tuned_recall, tuned_f1]
    
    x = np.arange(len(metrics))
    width = 0.35
    
    plt.bar(x - width/2, before, width, label='调参前', color='lightblue')
    plt.bar(x + width/2, after, width, label='调参后', color='lightgreen')
    
    plt.xlabel('评价指标')
    plt.ylabel('得分')
    plt.title('调参前后模型性能比较')
    plt.xticks(x, metrics)
    plt.legend()
    plt.ylim(0, 1.0)
    
    # 添加数值标签
    for i, v in enumerate(before):
        plt.text(i - width/2, v + 0.02, f'{v:.4f}', ha='center')
    
    for i, v in enumerate(after):
        plt.text(i + width/2, v + 0.02, f'{v:.4f}', ha='center')
    
    plt.tight_layout()
    plt.savefig('output/model_comparison.png', dpi=300)
    
    # 创建诊断结果分布表格图
    plt.figure(figsize=(8, 4))
    ax = plt.subplot(111, frame_on=False)
    ax.xaxis.set_visible(False)
    ax.yaxis.set_visible(False)
    
    diagnosis_df = pd.DataFrame({
        'diagnosis': ['0 (良性)', '1 (恶性)'],
        'count': [diagnosis_count[0], diagnosis_count[1]]
    })
    
    table = plt.table(
        cellText=diagnosis_df.values,
        colLabels=diagnosis_df.columns,
        loc='center',
        cellLoc='center',
        colColours=['#f2f2f2', '#f2f2f2'],
        cellColours=[['#e6f3ff', '#e6f3ff'], ['#ffe6e6', '#ffe6e6']]
    )
    
    table.auto_set_font_size(False)
    table.set_fontsize(12)
    table.scale(1.2, 2)
    
    plt.title('诊断结果分布表', y=0.8)
    plt.tight_layout()
    plt.savefig('output/diagnosis_table.png', dpi=300)
    
    # 创建分类报告表格图
    plt.figure(figsize=(12, 6))
    ax = plt.subplot(111, frame_on=False)
    ax.xaxis.set_visible(False)
    ax.yaxis.set_visible(False)
    
    # 提取分类报告数据
    report_table = pd.DataFrame({
        '类别': ['0 (良性)', '1 (恶性)', 'accuracy', 'macro avg', 'weighted avg'],
        'precision': [f"{report['0']['precision']:.2f}", f"{report['1']['precision']:.2f}", 
                     '', f"{report['macro avg']['precision']:.2f}", f"{report['weighted avg']['precision']:.2f}"],
        'recall': [f"{report['0']['recall']:.2f}", f"{report['1']['recall']:.2f}", 
                  '', f"{report['macro avg']['recall']:.2f}", f"{report['weighted avg']['recall']:.2f}"],
        'f1-score': [f"{report['0']['f1-score']:.2f}", f"{report['1']['f1-score']:.2f}", 
                    f"{report['accuracy']:.2f}", f"{report['macro avg']['f1-score']:.2f}", f"{report['weighted avg']['f1-score']:.2f}"],
        'support': [f"{report['0']['support']}", f"{report['1']['support']}", 
                   f"{report['0']['support'] + report['1']['support']}", 
                   f"{report['0']['support'] + report['1']['support']}", 
                   f"{report['0']['support'] + report['1']['support']}"]
    })
    
    table = plt.table(
        cellText=report_table.values,
        colLabels=report_table.columns,
        loc='center',
        cellLoc='center',
        colColours=['#f2f2f2', '#f2f2f2', '#f2f2f2', '#f2f2f2', '#f2f2f2'],
        cellColours=[
            ['#e6f3ff', '#e6f3ff', '#e6f3ff', '#e6f3ff', '#e6f3ff'],
            ['#ffe6e6', '#ffe6e6', '#ffe6e6', '#ffe6e6', '#ffe6e6'],
            ['#f2f2f2', '#f2f2f2', '#f2f2f2', '#f2f2f2', '#f2f2f2'],
            ['#f2f2f2', '#f2f2f2', '#f2f2f2', '#f2f2f2', '#f2f2f2'],
            ['#f2f2f2', '#f2f2f2', '#f2f2f2', '#f2f2f2', '#f2f2f2']
        ]
    )
    
    table.auto_set_font_size(False)
    table.set_fontsize(12)
    table.scale(1.2, 1.5)
    
    plt.title('分类报告', y=0.85)
    plt.tight_layout()
    plt.savefig('output/classification_report_table.png', dpi=300)
    
    # 创建最佳参数和模型性能表格图
    plt.figure(figsize=(10, 6))
    ax = plt.subplot(111, frame_on=False)
    ax.xaxis.set_visible(False)
    ax.yaxis.set_visible(False)
    
    # 创建最佳参数和性能数据
    performance_table = pd.DataFrame({
        '指标': ['最佳参数', '调优后准确率', '调优后精确率', '调优后召回率', '调优后F1值'],
        '值': [f"var_smoothing: {grid_search.best_params_['var_smoothing']}", 
              f"{tuned_accuracy:.6f}", 
              f"{tuned_precision:.6f}", 
              f"{tuned_recall:.6f}", 
              f"{tuned_f1:.6f}"]
    })
    
    table = plt.table(
        cellText=performance_table.values,
        colLabels=performance_table.columns,
        loc='center',
        cellLoc='center',
        colColours=['#f2f2f2', '#f2f2f2'],
        cellColours=[
            ['#e6f9ff', '#e6f9ff'],
            ['#e6f9ff', '#e6f9ff'],
            ['#e6f9ff', '#e6f9ff'],
            ['#e6f9ff', '#e6f9ff'],
            ['#e6f9ff', '#e6f9ff']
        ]
    )
    
    table.auto_set_font_size(False)
    table.set_fontsize(12)
    table.scale(1.2, 1.5)
    
    plt.title('最佳参数和模型性能', y=0.85)
    plt.tight_layout()
    plt.savefig('output/best_parameters_performance.png', dpi=300)
    
    # 创建调参前后性能比较表格图
    plt.figure(figsize=(12, 6))
    ax = plt.subplot(111, frame_on=False)
    ax.xaxis.set_visible(False)
    ax.yaxis.set_visible(False)
    
    comparison_table = pd.DataFrame({
        '指标': ['准确率', '精确率', '召回率', 'F1值'],
        '调参前': [f"{accuracy:.6f}", f"{precision:.6f}", f"{recall:.6f}", f"{f1:.6f}"],
        '调参后': [f"{tuned_accuracy:.6f}", f"{tuned_precision:.6f}", f"{tuned_recall:.6f}", f"{tuned_f1:.6f}"]
    })
    
    table = plt.table(
        cellText=comparison_table.values,
        colLabels=comparison_table.columns,
        loc='center',
        cellLoc='center',
        colColours=['#f2f2f2', '#f2f2f2', '#f2f2f2'],
        cellColours=[
            ['#f2f2f2', '#e6f3ff', '#e6ffe6'],
            ['#f2f2f2', '#e6f3ff', '#e6ffe6'],
            ['#f2f2f2', '#e6f3ff', '#e6ffe6'],
            ['#f2f2f2', '#e6f3ff', '#e6ffe6']
        ]
    )
    
    table.auto_set_font_size(False)
    table.set_fontsize(12)
    table.scale(1.2, 1.5)
    
    plt.title('调参前后性能比较', y=0.85)
    plt.tight_layout()
    plt.savefig('output/performance_comparison_table.png', dpi=300)
    
    print("\n分析完成，结果已保存在output目录下。")

if __name__ == "__main__":
    main()
